//
// Created by root on 2/23/18.
//

#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include "loaderengine.h"
#include "../premise/premise.h"
#include "common.h"
#include "../config/sys.h"
#include "../trans/parser.h"
#include "../trans/matcher.h"


static String get_real_path(String path) {
    char actualpath [PATH_MAX];

    if (path == NULL) {
        return NULL;
    }
    if (*path == '/') {
        return path;
    }
    realpath(path, actualpath);
    return copy_string(actualpath);
}

static void match_template(Statement *source) {
    State *start = NULL;
    Queue *repQueue = init_queue();
    Mapping *mapping = get_sys_mapping(get_mappings(source), MACRO_DEF_TEMPLATE);

    start = test_match(mapping, source, repQueue);
    if (start->accept) {
        eval_sys(mapping, repQueue);
    }
}

static void* process_load_template(FILE *fp) {
    Statement *s = NULL;
    while (has_next_statement(fp)) {
        s = next_statement(fp);
        match_template(s);
    }
    return NULL;
}

String loadtemplate(Statement *s) {
    String file = strdup(extract_content(s));
    String path = get_real_path(file);
    //printf("file:%s\n", file);
    //printf("path:%s\n", path);
    return read_file(path, process_load_template);
}

static void* process_load_file(FILE *fp) {
    Statement *s = NULL;
    while (has_next_statement(fp)) {
        s = next_statement(fp);
        match_internal(s);
    }
    return NULL;
}

String loadfile(Statement *s) {
    String file = strdup(extract_content(s));
    String path = get_real_path(file);
    //printf("file:%s\n", file);
    //printf("path:%s\n", path);
    return read_file(path, process_load_file);
}

static void* process_load_package(FILE *fp) {
    Statement *s = NULL;
    while (has_next_statement(fp)) {
        s = next_statement(fp);
        //todo
    }
    return NULL;
}

String loadpackage(Statement *s) {
    String file = strdup(extract_content(s));
    String path = get_real_path(file);
    //printf("file:%s\n", file);
    //printf("path:%s\n", path);
    return read_file(path, process_load_package);
}

String loadmodule(Statement *s) {
    //return load(s, FILE_TYPE_FILE);
}

String loadcomponent(Statement *s) {
    return NULL;
}


static String currentComponent;
static Map componentMap;
static Map dependentMap;

String defcomp(Statement *s) {
    currentComponent = extract_content(s);
    String basePath;
    int result = hashmap_get(globalFunny->componentPaths, currentComponent, (void**)(&basePath));
    if (result == MAP_MISSING) {
        fprintf(stderr, "Cannot find component: %s\n", currentComponent);
        exit(EXIT_FAILURE);
    }
    String componentPath = append_path(basePath, currentComponent);
    String componentMetaFile = append_path(componentPath, COMPONENT_META_FILE);
    Component *component = (Component *)read_file(componentMetaFile, build_component);
    component->name = currentComponent;

    if (componentMap == NULL) {
        componentMap = hashmap_new();
    }
    hashmap_put(componentMap, component->name, component);
    return NULL;
}

static int detect_circle_recursive(String start, String *dependents) {
    String *nextDependents;
    while (*dependents != NULL) {
        if (equals_string(start, *dependents)) {
            return TRUE;
        }
        int result = hashmap_get(dependentMap, *dependents, (void**)(&nextDependents));
        if (result == MAP_OK) {
            return detect_circle_recursive(start, nextDependents);
        }
        dependents++;
    }
    return FALSE;
}

static int detect_circle(String component) {
    if (dependentMap == NULL) {
        return FALSE;
    }
    String *dependents;
    int result = hashmap_get(dependentMap, component, (void**)(&dependents));
    if (result == MAP_OK) {
        return detect_circle_recursive(component, dependents);
    }
    return FALSE;
}

String require(Statement *s) {
    String* componentNames = split_string(extract_content(s), ' '); //todo with trim
    String basePath;
    String componentPath;
    String componentFilePath;
    int result;

    if (dependentMap == NULL) {
        dependentMap = hashmap_new();
    }
    hashmap_put(dependentMap, currentComponent, componentNames);

    if (detect_circle(currentComponent)) {
        fprintf(stderr, "Circular dependencies found in component: %s\n", currentComponent);
        exit(EXIT_FAILURE);
    }

    while (*componentNames != NULL) {
        cache_component_path(*componentNames);
        result = hashmap_get(globalFunny->componentPaths, *componentNames, (void**)(&basePath));
        if (result == MAP_MISSING) {
            fprintf(stderr, "Cannot find component: %s\n", *componentNames);
            exit(EXIT_FAILURE);
        }
        componentPath = append_path(basePath, *componentNames);
        componentFilePath = append_path(componentPath, COMPONENT_FILE);
        read_file(componentFilePath, explain);

        componentNames++;
    }

    return NULL;
}

void construct_graph() {
    int i = 0;
    String *keys = hashmap_keys(componentMap);
    Component *component;
    String *dependents;

    for (i = 0; i< hashmap_length(componentMap); i++) {
        if (keys[i]) {
            hashmap_get(componentMap, keys[i], (void **) (&component));
            add_vertex(globalFunny->components, component->name, component);
        }
    }

    keys = hashmap_keys(dependentMap);
    for (i = 0; i< hashmap_length(dependentMap); i++) {
        if (keys[i]) {
            hashmap_get(dependentMap, keys[i], (void **) (&dependents));
            while (*dependents != NULL) {
                add_edge(globalFunny->components, keys[i], *dependents);
                dependents++;
            }

        }
    }
}


void* explain(FILE* fp) {
    Statement *s = NULL;
    while (has_next_statement(fp)) {
        s = next_statement(fp);
        match_internal(s);
    }
    return NULL;
}

